<template>
  <div class="avatar-cropper">
    <el-dialog
      :model-value="visible"
      @update:model-value="visible = $event"
      title="裁剪头像"
      width="600px"
      :before-close="handleClose"
      :close-on-click-modal="false"
    >
      <div class="cropper-container">
        <div class="cropper-wrapper">
          <div class="image-container" ref="imageContainer">
            <img
              ref="imageRef"
              :src="imageUrl"
              @load="handleImageLoad"
              style="max-width: 100%; max-height: 400px; display: block;"
            />
            <div
              v-if="showCropBox"
              class="crop-box"
              :style="cropBoxStyle"
              @mousedown="startDrag"
            >
              <div 
                class="crop-handle crop-handle-nw" 
                @mousedown.stop="(e) => startResize('nw', e)"
                @mouseenter="setCursor('nw-resize')"
                @mouseleave="resetCursor()"
              ></div>
              <div 
                class="crop-handle crop-handle-ne" 
                @mousedown.stop="(e) => startResize('ne', e)"
                @mouseenter="setCursor('ne-resize')"
                @mouseleave="resetCursor()"
              ></div>
              <div 
                class="crop-handle crop-handle-sw" 
                @mousedown.stop="(e) => startResize('sw', e)"
                @mouseenter="setCursor('sw-resize')"
                @mouseleave="resetCursor()"
              ></div>
              <div 
                class="crop-handle crop-handle-se" 
                @mousedown.stop="(e) => startResize('se', e)"
                @mouseenter="setCursor('se-resize')"
                @mouseleave="resetCursor()"
              ></div>
            </div>
          </div>
          
          <div class="crop-controls">
            <el-button @click="resetCrop">重置</el-button>
            <el-button @click="zoomIn">放大</el-button>
            <el-button @click="zoomOut">缩小</el-button>
          </div>
        </div>
        
        <div class="preview-section">
          <h4>预览</h4>
          <div class="preview-container">
            <canvas
              ref="previewCanvas"
              width="120"
              height="120"
              class="preview-avatar"
            ></canvas>
            <p class="preview-text">头像预览</p>
          </div>
        </div>
      </div>
      
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="handleClose">取消</el-button>
          <el-button type="primary" @click="handleConfirm" :loading="uploading">
            确认裁剪
          </el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import api from '@/utils/api'

const props = defineProps<{
  modelValue: boolean
  imageUrl: string
}>()

const emit = defineEmits<{
  'update:modelValue': [value: boolean]
  'cropped': [url: string]
}>()

const imageRef = ref<HTMLImageElement>()
const imageContainer = ref<HTMLDivElement>()
const previewCanvas = ref<HTMLCanvasElement>()
const uploading = ref(false)
const showCropBox = ref(false)

// 裁剪框相关状态
const cropBox = ref({
  x: 0,
  y: 0,
  width: 80,   // 进一步减小初始尺寸
  height: 80
})

const isDragging = ref(false)
const isResizing = ref(false)
const resizeDirection = ref('')
const dragStart = ref({ x: 0, y: 0, width: 0, height: 0 })
const imageScale = ref(1)
const imageOffset = ref({ x: 0, y: 0 })

const visible = computed({
  get: () => props.modelValue,
  set: (value) => emit('update:modelValue', value)
})

const cropBoxStyle = computed(() => ({
  left: `${cropBox.value.x}px`,
  top: `${cropBox.value.y}px`,
  width: `${cropBox.value.width}px`,
  height: `${cropBox.value.height}px`
}))

const handleImageLoad = () => {
  nextTick(() => {
    if (imageRef.value && imageContainer.value) {
      const img = imageRef.value
      const container = imageContainer.value
      
      // 等待图片完全加载
      setTimeout(() => {
        // 计算图片在容器中的实际显示尺寸和位置
        const containerRect = container.getBoundingClientRect()
        const imgRect = img.getBoundingClientRect()
        
        // 计算图片相对于容器的实际位置和尺寸
        const displayWidth = img.offsetWidth
        const displayHeight = img.offsetHeight
        
        // 计算图片在容器中的偏移量（考虑留白）
        const offsetX = (container.offsetWidth - displayWidth) / 2
        const offsetY = (container.offsetHeight - displayHeight) / 2
        
        // 计算缩放比例
        imageScale.value = displayWidth / img.naturalWidth
        
        // 设置裁剪框初始位置（居中，但要在图片范围内）
        cropBox.value.x = Math.max(0, (displayWidth - cropBox.value.width) / 2)
        cropBox.value.y = Math.max(0, (displayHeight - cropBox.value.height) / 2)
        
        // 保存图片在容器中的偏移量，用于边界计算
        imageOffset.value = { x: offsetX, y: offsetY }
        
        showCropBox.value = true
        updatePreview()
        
        console.log('Image loaded:', {
          naturalWidth: img.naturalWidth,
          naturalHeight: img.naturalHeight,
          displayWidth,
          displayHeight,
          offsetX,
          offsetY,
          scale: imageScale.value,
          cropBox: cropBox.value,
          containerWidth: container.offsetWidth,
          containerHeight: container.offsetHeight,
          maxPossibleX: displayWidth - cropBox.value.width,
          maxPossibleY: displayHeight - cropBox.value.height
        })
      }, 100)
    }
  })
}

const startDrag = (e: MouseEvent) => {
  if (isResizing.value || !imageRef.value || !imageContainer.value) return
  
  isDragging.value = true
  const container = imageContainer.value
  const rect = container.getBoundingClientRect()
  
  // 计算相对于容器的鼠标位置
  const mouseX = e.clientX - rect.left
  const mouseY = e.clientY - rect.top
  
  // 计算相对于图片的鼠标位置（考虑图片在容器中的偏移）
  const relativeMouseX = mouseX - imageOffset.value.x
  const relativeMouseY = mouseY - imageOffset.value.y
  
  dragStart.value = {
    x: relativeMouseX - cropBox.value.x,
    y: relativeMouseY - cropBox.value.y,
    width: 0,
    height: 0
  }
  
  console.log('Start drag:', {
    mouseX,
    mouseY,
    relativeMouseX,
    relativeMouseY,
    cropBox: cropBox.value,
    imageOffset: imageOffset.value,
    dragStart: dragStart.value
  })
  
  document.addEventListener('mousemove', handleDrag)
  document.addEventListener('mouseup', stopDrag)
  e.preventDefault()
}

const startResize = (direction: string, e: MouseEvent) => {
  if (!imageRef.value) return
  
  // 阻止事件冒泡，防止触发图片拖拽
  e.preventDefault()
  e.stopPropagation()
  
  isResizing.value = true
  resizeDirection.value = direction
  
  // 保存当前选区的状态
  dragStart.value = {
    x: cropBox.value.x,
    y: cropBox.value.y,
    width: cropBox.value.width,
    height: cropBox.value.height
  }
  
  console.log('Start resize:', {
    direction,
    startX: dragStart.value.x,
    startY: dragStart.value.y,
    startWidth: dragStart.value.width,
    startHeight: dragStart.value.height
  })
  
  document.addEventListener('mousemove', handleResize)
  document.addEventListener('mouseup', stopResize)
}

const handleDrag = (e: MouseEvent) => {
  if (!isDragging.value || isResizing.value) return
  
  if (!imageRef.value || !imageContainer.value) return
  
  const img = imageRef.value
  const container = imageContainer.value
  const rect = container.getBoundingClientRect()
  
  // 计算相对于容器的鼠标位置
  const mouseX = e.clientX - rect.left
  const mouseY = e.clientY - rect.top
  
  // 计算相对于图片的鼠标位置（考虑图片在容器中的偏移）
  const relativeMouseX = mouseX - imageOffset.value.x
  const relativeMouseY = mouseY - imageOffset.value.y
  
  // 计算新的选区位置
  const newX = relativeMouseX - dragStart.value.x
  const newY = relativeMouseY - dragStart.value.y
  
  // 严格限制在图片实际显示范围内
  const maxX = Math.max(0, img.offsetWidth - cropBox.value.width)
  const maxY = Math.max(0, img.offsetHeight - cropBox.value.height)
  
  // 计算边界限制前的值
  const beforeClampX = newX
  const beforeClampY = newY
  
  cropBox.value.x = Math.max(0, Math.min(newX, maxX))
  cropBox.value.y = Math.max(0, Math.min(newY, maxY))
  
  // 检查是否被边界限制
  const wasClampedX = beforeClampX !== cropBox.value.x
  const wasClampedY = beforeClampY !== cropBox.value.y
  
  // 调试信息
  console.log('Drag bounds:', {
    containerWidth: container.offsetWidth,
    containerHeight: container.offsetHeight,
    imgWidth: img.offsetWidth,
    imgHeight: img.offsetHeight,
    cropWidth: cropBox.value.width,
    cropHeight: cropBox.value.height,
    maxX,
    maxY,
    mouseX,
    mouseY,
    relativeMouseX,
    relativeMouseY,
    newX,
    newY,
    beforeClampX,
    beforeClampY,
    finalX: cropBox.value.x,
    finalY: cropBox.value.y,
    wasClampedX,
    wasClampedY,
    imageOffset: imageOffset.value,
    canReachRight: maxX > 0,
    canReachBottom: maxY > 0
  })
  
  updatePreview()
}

const stopDrag = () => {
  isDragging.value = false
  document.removeEventListener('mousemove', handleDrag)
  document.removeEventListener('mouseup', stopDrag)
}

const handleResize = (e: MouseEvent) => {
  if (!isResizing.value || !imageRef.value || !imageContainer.value) return
  
  // 阻止默认行为和事件冒泡
  e.preventDefault()
  e.stopPropagation()
  
  const container = imageContainer.value
  const rect = container.getBoundingClientRect()
  
  // 计算相对于容器的鼠标位置
  const mouseX = e.clientX - rect.left
  const mouseY = e.clientY - rect.top
  
  // 计算相对于图片的鼠标位置（考虑图片在容器中的偏移）
  const relativeMouseX = mouseX - imageOffset.value.x
  const relativeMouseY = mouseY - imageOffset.value.y
  
  // 获取初始状态
  const startX = dragStart.value.x
  const startY = dragStart.value.y
  const startWidth = dragStart.value.width
  const startHeight = dragStart.value.height
  
  let newX = startX
  let newY = startY
  let newWidth = startWidth
  let newHeight = startHeight
  
  switch (resizeDirection.value) {
    case 'nw':
      // 左上角：鼠标位置作为新的左上角
      newX = mouseX
      newY = mouseY
      newWidth = startX + startWidth - mouseX
      newHeight = startY + startHeight - mouseY
      break
    case 'ne':
      // 右上角：保持左上角位置，调整宽度和高度
      newX = startX
      newY = mouseY
      newWidth = mouseX - startX
      newHeight = startY + startHeight - mouseY
      break
    case 'sw':
      // 左下角：保持右上角位置，调整宽度和高度
      newX = mouseX
      newY = startY
      newWidth = startX + startWidth - mouseX
      newHeight = mouseY - startY
      break
    case 'se':
      // 右下角：保持左上角位置，调整宽度和高度
      newX = startX
      newY = startY
      newWidth = mouseX - startX
      newHeight = mouseY - startY
      break
  }
  
  // 限制最小和最大尺寸
  const img = imageRef.value
  const minSize = 10  // 进一步降低最小尺寸限制
  const maxSize = Math.min(img.offsetWidth, img.offsetHeight)
  
  newWidth = Math.max(minSize, Math.min(newWidth, maxSize))
  newHeight = Math.max(minSize, Math.min(newHeight, maxSize))
  
  // 强制保持正方形
  const size = Math.min(newWidth, newHeight)
  newWidth = size
  newHeight = size
  
  // 调整位置
  if (resizeDirection.value.includes('w')) {
    newX = cropBox.value.x + cropBox.value.width - newWidth
  }
  if (resizeDirection.value.includes('n')) {
    newY = cropBox.value.y + cropBox.value.height - newHeight
  }
  
  // 严格限制在图片实际显示范围内
  const maxX = Math.max(0, img.offsetWidth - newWidth)
  const maxY = Math.max(0, img.offsetHeight - newHeight)
  
  // 确保选区完全在图片范围内
  cropBox.value.x = Math.max(0, Math.min(newX, maxX))
  cropBox.value.y = Math.max(0, Math.min(newY, maxY))
  cropBox.value.width = newWidth
  cropBox.value.height = newHeight
  
  // 最终检查：确保选区不会超出图片边界
  if (cropBox.value.x + cropBox.value.width > img.offsetWidth) {
    cropBox.value.width = img.offsetWidth - cropBox.value.x
  }
  if (cropBox.value.y + cropBox.value.height > img.offsetHeight) {
    cropBox.value.height = img.offsetHeight - cropBox.value.y
  }
  
  console.log('Resize calculation:', {
    direction: resizeDirection.value,
    mouseX,
    mouseY,
    relativeMouseX,
    relativeMouseY,
    startX,
    startY,
    startWidth,
    startHeight,
    newX,
    newY,
    newWidth,
    newHeight,
    finalX: cropBox.value.x,
    finalY: cropBox.value.y,
    finalWidth: cropBox.value.width,
    finalHeight: cropBox.value.height,
    imageOffset: imageOffset.value
  })
  
  updatePreview()
}

const stopResize = (e?: MouseEvent) => {
  if (e) {
    e.preventDefault()
    e.stopPropagation()
  }
  
  isResizing.value = false
  resizeDirection.value = ''
  document.removeEventListener('mousemove', handleResize)
  document.removeEventListener('mouseup', stopResize)
  resetCursor()
}

const setCursor = (cursor: string) => {
  if (imageRef.value) {
    imageRef.value.style.cursor = cursor
  }
}

const resetCursor = () => {
  if (imageRef.value) {
    imageRef.value.style.cursor = 'default'
  }
}

const resetCrop = () => {
  if (imageRef.value) {
    const img = imageRef.value
    cropBox.value.x = (img.offsetWidth - cropBox.value.width) / 2
    cropBox.value.y = (img.offsetHeight - cropBox.value.height) / 2
    updatePreview()
  }
}

const zoomIn = () => {
  imageScale.value = Math.min(imageScale.value * 1.2, 3)
  if (imageRef.value) {
    imageRef.value.style.transform = `scale(${imageScale.value})`
  }
  updatePreview()
}

const zoomOut = () => {
  imageScale.value = Math.max(imageScale.value / 1.2, 0.5)
  if (imageRef.value) {
    imageRef.value.style.transform = `scale(${imageScale.value})`
  }
  updatePreview()
}

const updatePreview = () => {
  if (!imageRef.value || !previewCanvas.value) return
  
  const img = imageRef.value
  const canvas = previewCanvas.value
  const ctx = canvas.getContext('2d')
  
  if (!ctx) return
  
  // 清空画布
  ctx.clearRect(0, 0, 120, 120)
  
  // 计算裁剪区域在原图中的位置
  // 使用图片的实际显示尺寸计算缩放比例
  const scaleX = img.naturalWidth / img.offsetWidth
  const scaleY = img.naturalHeight / img.offsetHeight
  
  // 计算源图片中的裁剪区域
  const sourceX = cropBox.value.x * scaleX
  const sourceY = cropBox.value.y * scaleY
  const sourceWidth = cropBox.value.width * scaleX
  const sourceHeight = cropBox.value.height * scaleY
  
  // 确保源区域在图片范围内
  const clampedSourceX = Math.max(0, Math.min(sourceX, img.naturalWidth - sourceWidth))
  const clampedSourceY = Math.max(0, Math.min(sourceY, img.naturalHeight - sourceHeight))
  const clampedSourceWidth = Math.min(sourceWidth, img.naturalWidth - clampedSourceX)
  const clampedSourceHeight = Math.min(sourceHeight, img.naturalHeight - clampedSourceY)
  
  console.log('Preview calculation:', {
    cropBox: cropBox.value,
    scaleX,
    scaleY,
    sourceX,
    sourceY,
    sourceWidth,
    sourceHeight,
    clampedSourceX,
    clampedSourceY,
    clampedSourceWidth,
    clampedSourceHeight,
    naturalWidth: img.naturalWidth,
    naturalHeight: img.naturalHeight,
    offsetWidth: img.offsetWidth,
    offsetHeight: img.offsetHeight,
    imageOffset: imageOffset.value
  })
  
  // 绘制裁剪后的图片
  ctx.drawImage(
    img,
    clampedSourceX, clampedSourceY, clampedSourceWidth, clampedSourceHeight,
    0, 0, 120, 120
  )
}

const handleClose = () => {
  visible.value = false
  showCropBox.value = false
}

const handleConfirm = async () => {
  if (!imageRef.value) return
  
  try {
    uploading.value = true
    
    // 创建临时canvas进行裁剪
    const canvas = document.createElement('canvas')
    const ctx = canvas.getContext('2d')
    
    if (!ctx) return
    
    canvas.width = 200
    canvas.height = 200
    
    const img = imageRef.value
    const scaleX = img.naturalWidth / img.offsetWidth
    const scaleY = img.naturalHeight / img.offsetHeight
    
    const sourceX = cropBox.value.x * scaleX
    const sourceY = cropBox.value.y * scaleY
    const sourceWidth = cropBox.value.width * scaleX
    const sourceHeight = cropBox.value.height * scaleY
    
    // 确保源区域在图片范围内
    const clampedSourceX = Math.max(0, Math.min(sourceX, img.naturalWidth - sourceWidth))
    const clampedSourceY = Math.max(0, Math.min(sourceY, img.naturalHeight - sourceHeight))
    const clampedSourceWidth = Math.min(sourceWidth, img.naturalWidth - clampedSourceX)
    const clampedSourceHeight = Math.min(sourceHeight, img.naturalHeight - clampedSourceY)
    
    // 绘制裁剪后的图片
    ctx.drawImage(
      img,
      clampedSourceX, clampedSourceY, clampedSourceWidth, clampedSourceHeight,
      0, 0, 200, 200
    )
    
    // 转换为blob
    const blob = await new Promise<Blob>((resolve) => {
      canvas.toBlob((blob: Blob | null) => {
        resolve(blob!)
      }, 'image/png', 0.9)
    })
    
    // 上传裁剪后的图片
    const formData = new FormData()
    formData.append('file', blob, 'avatar.png')
    
    const response = await api.post('/posts/upload-image', formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    })
    
    // 返回裁剪后的图片URL
    emit('cropped', response.data.url)
    ElMessage.success('头像上传成功')
    handleClose()
    
  } catch (error) {
    console.error('头像上传失败:', error)
    ElMessage.error('头像上传失败，请重试')
  } finally {
    uploading.value = false
  }
}

// 监听图片URL变化
watch(() => props.imageUrl, (newUrl) => {
  if (newUrl) {
    showCropBox.value = false
  }
})
</script>

<style scoped>
.avatar-cropper {
  .cropper-container {
    display: flex;
    gap: 2rem;
    align-items: flex-start;
  }
  
  .cropper-wrapper {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 1rem;
  }
  
  .image-container {
    position: relative;
    border: 1px solid #dcdfe6;
    border-radius: 6px;
    overflow: hidden;
    background: #f5f7fa;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 300px;
  }
  
  .crop-box {
    position: absolute;
    border: 2px solid #409eff;
    background: rgba(64, 158, 255, 0.1);
    cursor: move;
  }
  
  .crop-handle {
    position: absolute;
    width: 8px;
    height: 8px;
    background: #409eff;
    border: 1px solid #fff;
    border-radius: 50%;
  }
  
  .crop-handle-nw {
    top: -4px;
    left: -4px;
    cursor: nw-resize;
  }
  
  .crop-handle-ne {
    top: -4px;
    right: -4px;
    cursor: ne-resize;
  }
  
  .crop-handle-sw {
    bottom: -4px;
    left: -4px;
    cursor: sw-resize;
  }
  
  .crop-handle-se {
    bottom: -4px;
    right: -4px;
    cursor: se-resize;
  }
  
  .crop-handle:hover {
    background: #409eff;
    transform: scale(1.2);
  }
  
  .crop-handle:active {
    background: #337ecc;
    transform: scale(1.1);
  }
  
  .crop-controls {
    display: flex;
    gap: 0.5rem;
    justify-content: center;
  }
  
  .preview-section {
    width: 200px;
    text-align: center;
  }
  
  .preview-section h4 {
    margin: 0 0 1rem 0;
    color: #333;
    font-size: 1rem;
  }
  
  .preview-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.5rem;
  }
  
  .preview-avatar {
    border-radius: 50%;
    border: 2px solid #dcdfe6;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  }
  
  .preview-text {
    font-size: 0.875rem;
    color: #666;
    margin: 0;
  }
  
  .dialog-footer {
    display: flex;
    justify-content: flex-end;
    gap: 1rem;
  }
}

@media (max-width: 768px) {
  .cropper-container {
    flex-direction: column;
    gap: 1rem;
  }
  
  .cropper-wrapper {
    width: 100%;
  }
  
  .preview-section {
    width: 100%;
  }
  
  .preview-container {
    flex-direction: row;
    justify-content: center;
    gap: 1rem;
  }
}
</style>